﻿using System;
using System.Collections.Generic;
using LoungeRepo.Contracts.EntityCache;

namespace LoungeRepo.EntityCache
{
    public class SecondLevelCache : IEntityCache
    {
        private readonly Dictionary<string, object> keyIndex = new Dictionary<string, object>();
        private readonly Dictionary<Type, Dictionary<object, object>> typeIndexes = new Dictionary<Type, Dictionary<object, object>>();


        public void Register(object entity, string id, string partition)
        {
            //TODO: should register overwrite an existing cache entry with the same key?

            var entityKey = CreateEntityKey(id, partition);
            if (!this.keyIndex.ContainsKey(entityKey))
            {
                this.keyIndex.Add(entityKey, entity);

                Dictionary<object, object> entityTypeIndex;
                if (!this.typeIndexes.TryGetValue(entity.GetType(), out entityTypeIndex))
                {
                    entityTypeIndex = new Dictionary<object, object>();
                    this.typeIndexes.Add(entity.GetType(), entityTypeIndex);
                }

                entityTypeIndex.Add(entity, entity);
            }
        }


        public void Evict(string id, string partition)
        {
            var entityKey = CreateEntityKey(id, partition);
            object entity;
            if (this.keyIndex.TryGetValue(entityKey, out entity))
            {
                this.typeIndexes[entity.GetType()].Remove(entity);
                this.keyIndex.Remove(entityKey);
            }
        }


        public IEnumerable<T> GetByType<T>()
        {
            if (this.typeIndexes.ContainsKey(typeof(T)))
            {
                List<T> entities = new List<T>();
                foreach (object entity in this.typeIndexes[typeof (T)].Values)
                    entities.Add((T) entity);
                return entities;
            }

            return new List<T>();
        }


        public bool TryGetByKey(string id, string partition, out object entity)
        {
            var entityKey = CreateEntityKey(id, partition);
            return this.keyIndex.TryGetValue(entityKey, out entity);
        }


        private string CreateEntityKey(string id, string partition)
        {
            return partition + "/" + id;
        }
    }
}
